package com.ggj2013.hbh;

import com.eclecticdesignstudio.motion.Actuate;
import haxe.Log;
import nme.display.Shape;
import nme.display.DisplayObject;
import com.ggj2013.hbh.utils.SoundPlayer;
import com.ggj2013.hbh.utils.HitBox;
import com.ggj2013.hbh.utils.CollisionDetection;
import nme.display.MovieClip;
import nme.display.GraphicsPath;
import nme.display.BlendMode;
import nme.display.Graphics;
import nme.display.GraphicsPathCommand;
import nme.display.Sprite;
import nme.text.TextFormatAlign;
import nme.text.TextFormat;
import nme.text.TextFieldAutoSize;
import nme.text.TextField;
import nme.geom.Matrix;
import nme.Vector;
import nme.events.Event;
import nme.display.Bitmap;
import com.ggj2013.hbh.utils.Tools;
import nme.geom.Point;
import nme.Lib;
import nme.display.GradientType;
import de.polygonal.ds.DLL;

import com.ggj2013.hbh.Visibility;

/**
 * @author Jonathan Moriarty
 */
class LevelScreen extends MovieClip 
{
	//Declare variables
	private var targetPoint:Point;
	private var _currTime:Int;
	private var _prevTime:Int;
	private var _deltaTime:Int;
	private var visMask:Shape;
	private var vis:Visibility;
	private var cutsceneDarkout:Shape;
	private var currentCutscene:Sprite;
	private var cutsceneImages:Array<Bitmap>;
	
	public static var inItemEvent:Bool;
	public static var inCutscene:Bool;
	public static var eventItem:Item;
	public static var itemEvent:ItemEvent;
	public static var inGameOver:Bool;
	public static var inTransfer:Bool;
	public static var playerWin:Bool;
	
	public static var levelNumber:Int = -1;
	inline public static var FINAL_LEVEL:Int = 2;
	public static var cutsceneFrame:Int;
	public static var CUTSCENE_FRAMES:Int = 2;
	
	public static var player:Player;
	private var sanityBar:SanityBar;
	public static var level:Level;
	private var playerPoint:Point;
	
	public var occlusions:Array<Segment>;
	public var rooms:Array<Dynamic>;
	public var curRoom:Room;
	// remove when unnecessary
	public var test:Door;
	//Set to false for performance boost!
	public var adjRooms = true;
	
	public function new() {
		super();
		//trace("CREATED");
		//trace( LevelScreen.levelNumber );
		LevelScreen.levelNumber++;
		
		//pretend room is generated at loadtime
		//var r = new Room("room1", "room1");
		//ok now real talk
		
		vis = new Visibility();
		
		SoundPlayer.initialize();
		
		level = new Level();
		addChild( level );
		
		// ui creation
		sanityBar = new SanityBar( Player.MAX_SANITY, Player.MIN_SANITY, Player.MAX_SANITY );
		sanityBar.x = 100;
		sanityBar.y = 500;
		addChild( sanityBar );
		
		player = new Player( HBH.STAGE_WIDTH / 2, HBH.STAGE_HEIGHT / 2 );
		player.x -= player.hitBox.width / 2;
		player.y -= player.hitBox.height / 2;
		addChild( player );
		
		visMask = new Shape();
		level.addChild( visMask );
		visMask.x = -Level.levelDiameter / 2;
		visMask.y = -Level.levelDiameter / 2;
		level.blendMode = BlendMode.LAYER;
		
		currentCutscene = new Sprite();
		cutsceneImages = new Array<Bitmap>();
		
		SoundPlayer.playHeartbeatSFX();
		blendMode = BlendMode.LAYER;
		
		curRoom = level.rooms[0];
		updateOcclusions();
		
		inItemEvent = false;
		inGameOver = false;
		inTransfer = false;
		playerWin = false;
		inCutscene = false;
		eventItem = null;
		itemEvent = null;
		
		if (levelNumber == 0)
		{
			inCutscene = true;
			cutsceneFrame = 1;
			
			createOpeningCutscene();
		}
	}
	
	private function createOpeningCutscene():Void
	{
		cutsceneDarkout = new Shape();
		cutsceneDarkout.graphics.beginFill( 0x000000 );
		cutsceneDarkout.graphics.drawRect( 0, 0, HBH.STAGE_WIDTH, HBH.STAGE_HEIGHT );
		cutsceneDarkout.graphics.endFill();
		cutsceneDarkout.alpha = .5;
		currentCutscene.addChild(cutsceneDarkout);
		
		var scene = Tools.loadBitmap("cutscene_text1");
		scene.alpha = 0;
		currentCutscene.addChild(scene);
		cutsceneImages.push(scene);
		
		addChild(currentCutscene);
		
		Actuate.tween(cutsceneImages[0], 2, { alpha:1 } );
	}
	
	private function advanceCutscene():Void
	{
		Actuate.tween(cutsceneImages[0], 2, { alpha:0 } ).onComplete(endOpeningCutscene);
	}
	
	private function endOpeningCutscene():Void
	{
		inCutscene = false;
		removeChild(currentCutscene);
	}
	
	private function updateVis() {
		var px = ( HBH.STAGE_WIDTH + Level.levelDiameter ) / 2 - level.x;
		var py = ( HBH.STAGE_HEIGHT + Level.levelDiameter ) / 2 - level.y;
		vis.setLightLocation( px, py );
		vis.sweep();
		visMask.graphics.clear();
		
		var visiblePolyPoints:Array<Visibility.Point> = vis.output;
		
		if ( visiblePolyPoints.length > 0 ) {
			var commands:Vector<Int> = new Vector<Int>();
			var data:Vector<Float> = new Vector<Float>();		
			for( p in visiblePolyPoints )
			{
				commands.push( GraphicsPathCommand.LINE_TO );
				data.push( p.x ); 
				data.push( p.y );
			}
			commands.push( GraphicsPathCommand.LINE_TO );
			
			var p:Visibility.Point = visiblePolyPoints[0];
			
			data.push(p.x); data.push(p.y);
			
			commands[0] = GraphicsPathCommand.MOVE_TO;
			var m:Matrix = new Matrix();
			var lightR = 600;
			m.createGradientBox(lightR, lightR, 0, px - lightR / 2, py - lightR / 2);
			visMask.graphics.beginFill(0xFFFF000, 0.0);
			visMask.graphics.drawRect(px - HBH.STAGE_WIDTH, py - HBH.STAGE_HEIGHT,px + HBH.STAGE_WIDTH, py + HBH.STAGE_HEIGHT);
			visMask.graphics.beginGradientFill(GradientType.RADIAL, 
					[0xFF0000, 0xFF0000, 0xFF0000, 0xFF0000], 
					[1.0, 0.8, 0.4, 0.2],
					[0,40,80,255], m);
			visMask.graphics.drawPath(commands, data);
			
			#if debug
				if (false)
			#end
			visMask.blendMode = BlendMode.ALPHA;
		}
	}
	
	private function blackoutComplete():Void
	{
		var gameOverFont = Tools.loadFont("jupiterc");
		
		var gameOverFormat = new TextFormat( gameOverFont.fontName, 48, 0x880000 );
		gameOverFormat.align = TextFormatAlign.CENTER;
		
		var replayFormat = new TextFormat( gameOverFont.fontName, 24, 0x880000 );
		replayFormat.align = TextFormatAlign.CENTER;
		
		
		var _gameOverDisplay = new TextField();
		_gameOverDisplay.defaultTextFormat = gameOverFormat;
		_gameOverDisplay.selectable = false;
		_gameOverDisplay.embedFonts = true;
		_gameOverDisplay.text = "You have descended into madness...\nDoomed, you wander alone save for \nthe beating of the hideous heart...";
		_gameOverDisplay.autoSize = nme.text.TextFieldAutoSize.CENTER;
		_gameOverDisplay.x = HBH.STAGE_WIDTH / 2 - _gameOverDisplay.width / 2;
		_gameOverDisplay.y = (HBH.STAGE_HEIGHT / 2  - _gameOverDisplay.height / 2) - 50;
		_gameOverDisplay.alpha = 0;
		addChild( _gameOverDisplay );
		
		Actuate.tween(_gameOverDisplay, 2, { alpha: 1 } );
		
		var _replayDisplay = new TextField();
		_replayDisplay.defaultTextFormat = replayFormat;
		_replayDisplay.selectable = false;
		_replayDisplay.embedFonts = true;
		_replayDisplay.text = "Press P to rety.\nPress M to return to menu.";
		_replayDisplay.autoSize = nme.text.TextFieldAutoSize.CENTER;
		_replayDisplay.x = HBH.STAGE_WIDTH / 2 - _replayDisplay.width / 2;
		_replayDisplay.y = (HBH.STAGE_HEIGHT / 2  - _replayDisplay.height / 2) + 50;
		_replayDisplay.alpha = 0;
		addChild( _replayDisplay );
		
		Actuate.tween(_replayDisplay, 2, { alpha: 1 } );
	}
	
	public function update( l_event:Event ):Void {
		var px:Float = ( HBH.STAGE_WIDTH + Level.levelDiameter ) / 2 - level.x;
		var py:Float = ( HBH.STAGE_HEIGHT + Level.levelDiameter ) / 2 - level.y;
	
		_currTime = Lib.getTimer();
		_deltaTime = _currTime - _prevTime;
		_prevTime = _currTime;
		//trace(_deltaTime);
		//trace( InputManager.isKeyPressed( InputManager.W_KEY )  );
		if ( !inGameOver && inTransfer )
		{
			inTransfer = false;
		}
		
		if ( !inGameOver && ( player.currentSanity <= 0 /*|| InputManager.isKeyPressed( InputManager.Q_KEY)*/ )  )
		{
			Tools.trace("game over");
			inGameOver = true;
			LevelScreen.levelNumber = -1;
			
			var blackout = new Sprite();
			blackout.graphics.beginFill( 0x000000);
			blackout.graphics.drawRect( 0, 0, HBH.STAGE_WIDTH, HBH.STAGE_HEIGHT );
			blackout.graphics.endFill();
			blackout.alpha = 0;
			addChild(blackout);
			
			Actuate.tween(blackout, 3, { alpha: 0.9 } ).onComplete(blackoutComplete);
			
			SoundPlayer.stopEndingSFX();
			SoundPlayer.stopMusic();
		}
		//trace("terms");
		//trace(inGameOver);
		//trace(inItemEvent);
		//trace(inTransfer);
		if ( !inGameOver && !inItemEvent && !inTransfer && !inCutscene )
		{
			//trace("gets here");
			level.update( _deltaTime );
			player.update( _deltaTime );
			sanityBar.update( player.currentSanity );
			
			//check for currentRoom Update
			if (curRoom.x > px  || px > curRoom.x + curRoom.width ||
				curRoom.y > py || py > curRoom.y + curRoom.height)
			{ 
				//room change
				for (room in curRoom.neighbors) 
				{	
					if (room == null) continue;
					if (room.x < px  && px < room.x + room.width &&
				room.y < py && py < room.y + room.height)
					{
						curRoom = level.rooms[ 0 ];
						updateOcclusions();
						break;
					}
				}
			}
			
			// heartbeat sound update
			var playerPoint = new Point( player.x, player.y );
			var relativeTargetPoint = level.localToGlobal((level.targetPoint));
			
			var xDistance = Math.abs( playerPoint.x - relativeTargetPoint.x );
			var yDistance = Math.abs( playerPoint.y - relativeTargetPoint.y );
			var averageDistance = ( xDistance + yDistance ) / 2;

			SoundPlayer.setHeartbeatDistance( averageDistance );
			SoundPlayer.checkSanity();
			
			updateVis();
		}
		
		if ( inGameOver && !inTransfer )
		{
			// TODO: stop all sound effects for the next run.
			if ( InputManager.isKeyPressed( InputManager.M_KEY ) )
			{
				SoundPlayer.stopHeartbeatSFX();
				dispatchEvent( new NavigationEvent( NavigationEvent.MENU ) );
				inTransfer = true;
			}
			else if ( InputManager.isKeyPressed( InputManager.P_KEY ) )
			{
				//trace("play again");
				//SoundPlayer.stopHeartbeatSFX();
				SoundPlayer.stopEndingSFX();
				SoundPlayer.stopMusic();
				SoundPlayer.stopHeartbeatSFX();
				dispatchEvent( new NavigationEvent( NavigationEvent.LEVEL ) );
				//InputManager.update();
				inTransfer = true;
			}
		}
		
		if ( inItemEvent )
		{
			//trace("item event");
			if ( itemEvent == null )
			{
				InputManager.update();
				itemEvent = new ItemEvent( eventItem );
				//addChildAt( itemEvent, getChildIndex( sanityBar ) );
				addChild( itemEvent );
			}
			
			itemEvent.update( _deltaTime ); 
			
			if ( itemEvent.remove )
			{
				//trace(itemEvent.item.type);
				itemEvent.visible = false;
				removeChild( itemEvent );
				if ( itemEvent.item.type == Item.LADDER )
				{
					playerWin = true;
					inGameOver = true;
					SoundPlayer.stopEndingSFX();
					SoundPlayer.stopMusic();
					SoundPlayer.stopHeartbeatSFX();
					dispatchEvent( new NavigationEvent( NavigationEvent.LEVEL ) );
				}
				
				else if ( itemEvent.item.type == Item.AIRLOCK_CONSOLE )
				{
					playerWin = true;
					inGameOver = true;
					SoundPlayer.stopEndingSFX();
					SoundPlayer.stopMusic();
					SoundPlayer.stopHeartbeatSFX();
					dispatchEvent( new NavigationEvent( NavigationEvent.MENU ) );
				}
				itemEvent = null;
				inItemEvent = false;
			}
		}
		
		if (inCutscene)
		{
			if ( InputManager.isKeyPressed( InputManager.E_KEY ) )
			{
				advanceCutscene();
			}
		}
		
		// test door update todo: move into player update
		/*if ( CollisionDetection.isPixelPerfectColliding( player.hitBox, test.triggerHitBox, this, true, 0 ) )
		{
			test.doorOpen = true;
		}
		else
		{
			test.doorOpen = false;
		}
		test.update( _deltaTime );*/
		
		//updateVis();
	}
	
	public static function handleWallCollision( l_room:Room, l_wall:HitBox, l_prevX:Float, l_prevY:Float ):Void
	{
		var l_wallX:Float = level.x + l_room.x + l_wall.x;
		var l_wallY:Float = level.y + l_room.y + l_wall.y;
	
		if ( level.x != l_prevX )
		{
			if ( level.x < l_prevX )
			{
				level.x += Math.abs( player.x + player.hitBox.width - l_wallX );
			}
			else
			{
				level.x -= Math.abs( l_wallX + l_wall.width - player.x );
			}
		}
		if ( level.y != l_prevY )
		{
			if ( level.y < l_prevY )
			{
				level.y += Math.abs( l_wallY - ( player.y + player.hitBox.height ) );
			}
			else
			{
				level.y -= Math.abs( l_wallY + l_wall.height - player.y );
			}
		}
	}

	private function updateOcclusions() {
		var px = HBH.STAGE_WIDTH / 2 - level.x;
		var py = HBH.STAGE_HEIGHT / 2 - level.y;
		var segs = [];
		var visDoors:Array<Door> = [];
		var l_furnitures:Array<Dynamic>;
		
		
		var visRooms:Array<Room> = new Array<Room>();
		visRooms.push(curRoom);
		
		if (adjRooms)
			for (room in curRoom.neighbors)
				if (room != null)
					visRooms.push(room);
					
		for ( room in visRooms ) {
			for (door in room.doors)
				if (!Lambda.has(visDoors, door))
					visDoors.push(door);
			segs = segs.concat(cast room.occlusions);
			l_furnitures = room.furnitures;
			for ( furniture in l_furnitures ) {
				segs = segs.concat( cast furniture.occlusions );
			}
		} 
		
		for ( door in visDoors ) {
			segs = segs.concat(cast door.occlusions);	
		}
		Tools.trace(Std.string(segs.length));
		
		vis.loadMap(Level.levelDiameter, 0, new Array<Visibility.Block>(), segs);
		updateVis();
	}
}